#ifndef GAME_DEBUG_H
#define GAME_DEBUG_H
#include "game_platform.h"
#include "game_file_format.h"
#include "game_asset.h"
#include "game.h"
#include "game_debug_interface.h"
#include "game_render_group.h"
#include "game_debug_ui.h"

enum debug_variable_to_text_flag {
    DEBUGVarToText_AddDebugUI = 1 << 0LL,      // 0b1
    DEBUGVarToText_AddName = 1 << 1LL,         // 0b10
    DEBUGVarToText_FloatSuffix = 1 << 2LL,     // 0b100
    DEBUGVarToText_LineFeedEnd = 1 << 3LL,     // 0b1000
    DEBUGVarToText_NullTerminator = 1 << 4LL,  // 0b10000
    DEBUGVarToText_Colon = 1 << 5LL,           // 0b100000
    DEBUGVarToText_PrettyBools = 1 << 6LL,     // 0b1000000
    DEBUGVarToText_StartAtLastSlash = 1 << 7LL,
    DEBUGVarToText_AddValue = 1 << 8LL,
};

enum debug_text_op {
    DEBUGTextOp_DrawText,
    DEBUGTextOp_SizeText,
};

struct debug_counter_snapshot {
    uint32 HitCount;
    uint64 CycleCount;
};

struct debug_counter_state {
    const char* FileName;
    const char* BlockName;
    uint32 LineNumber;
};
struct debug_variable_link;
struct debug_profile_node;
struct debug_stored_event;
struct debug_frame {
    uint64 BeginClock;
    uint64 EndClock;
    uint32 RegionCount;
    real32 FrameBarScale;
    uint32 FrameIndex;
    real32 WallSecondsElapsed;
    uint32 ProfileBlockCount;
    uint32 DataBlockCount;
    uint32 StoreEventCount;

    debug_stored_event* RootProfileNode;
};
struct debug_element;
struct debug_stored_event;
struct open_debug_block {
    uint32 StartingFrameIndex;
    debug_variable_link* Group;
    debug_record* Source;
    uint64 BeginClock;
    debug_element* Element;
    union {
        open_debug_block* Parent;
        open_debug_block* NextFree;
    };
    debug_stored_event* Node;
    debug_stored_event* FirstChild;
    debug_stored_event* LastChild;
};
struct debug_thread {
    uint32 ID;
    uint32 LaneIndex;
    open_debug_block* FirstOpenCodeBlock;
    open_debug_block* FirstOpenDataBlock;
    union {
        debug_thread* Next;
        debug_thread* NextFree;
    };
};

struct render_group;
struct game_assets;
struct font_id;

struct debug_tree_entry;

struct debug_view_inline_block {
    v2 Dim;
};

struct debug_view_arena_graph {
    debug_view_inline_block Block;
};

struct debug_view_profile_graph {
    debug_view_inline_block Block;
    const char* GUID;
};

enum debug_view_type {
    DebugViewType_Unknow,
    DebugViewType_Basic,
    DebugViewType_InlineBlock,
    DebugViewType_Collapsible,
};
struct debug_tree {
    v2 UIP;
    debug_variable_link* Group;
    debug_tree* Next;
    debug_tree* Prev;
};

struct debug_view_collapsible {
    bool32 ExpandedAlways;
    bool32 ExpandedAltView;
};
struct debug_view {
    debug_id ID;
    debug_view_type Type;
    debug_view* NextInHash;
    union {
        debug_view_inline_block InlineBlock;
        debug_view_profile_graph ProfileGraph;
        debug_view_collapsible Collapsible;
        debug_view_arena_graph ArenaGraph;
    };
};
struct debug_profile_node {
    debug_element* Element;
    debug_stored_event* FirstChild;
    debug_stored_event* NextSameParent;
    uint64 FrameRelativeClock;
    uint64 Duration;
    uint64 DurationOfChildren;
    uint32 ParentRelativeClock;
    uint32 Reserved;
    uint16 ThreadOrdinal;
    uint16 CoreIndex;
};

struct debug_stored_event {
    union {
        debug_stored_event* Next;
        debug_stored_event* NextFree;
    };

    uint32 FrameIndex;
    union {
        debug_event Event;
        debug_profile_node ProfileNode;
    };
};

struct debug_element_frame {
    uint64 TotalClocks;
    debug_stored_event* OldestEvent;
    debug_stored_event* MostRecentEvent;
};
#define DEBUG_FRAME_COUNT 256
struct debug_element {
    const char* OriginalGUID;
    const char* GUID;
    uint32 NameStartsAt;
    uint32 FileNameCount;
    uint32 LineNumber;
    bool32 ValueWasEdited;
    debug_type Type;
    debug_element_frame Frames[DEBUG_FRAME_COUNT];
    debug_element* NextInHash;
};

struct debug_state {
    uint32 DefaultClipRect;
    layout MouseTextLayout;
    v2 MouseText;
    uint32 BeginBlockCount = 0;
    uint32 EndBlockCount = 0;
    object_transform TextTransform;
    object_transform ShadowTransform;
    object_transform BackingTransform;
    object_transform UITransform;
    object_transform ToolTipTransform;

    debug_stored_event* FirstFreeStoredEvent;
    debug_view* ViewHash[4096];
    debug_element* ElementsHash[256];
    debug_variable_link* RootGroup;
    debug_variable_link* ProfileGroup;
    debug_tree TreeSentinel;
    debug_tree Tree;

    v2 LastMouseP;
    bool32 AltUI;
    // 【interact】 vi. 互相影响, 交互作用 n. 幕间剧, 幕间休息
    debug_interaction Interaction;
    debug_interaction HotInteraction;
    debug_interaction NextHotInteraction;

    bool32 Compiling;
#if GAME_INTERNAL
    debug_executing_process Compiler;
#endif  // GAME_INTERNAL
    v2 MenuP;
    bool32 MenuActive;
    uint32 HotMenuIndex;
    uint32 SelectedIDCount;
    debug_id SelectedID[64];
    loaded_font* DebugFont;
    hha_font* DebugFontInfo;
    platform_work_queue* HighPriorityQueue;
    bool32 IsInitialized;
    bool32 Paused;
    memory_arena DebugArena;
    memory_arena PerFrameArena;

    uint32 FrameBarLaneCount;
    real32 FrameBarScale;

    uint32 TotalFrameCount;
    uint32 ViewingFrameOrdinal;
    uint32 MostRecentFrameOrdinal;
    uint32 CollatingFrameOrdinal;
    uint32 OldestFrameOrdinal;
    uint32 NextFreeFrame;
    uint32 FrameCount;
    debug_frame Frames[DEBUG_FRAME_COUNT];
    debug_element* RootProfileElement;

    debug_frame CollationFrame;

    debug_thread* FirstThread;
    debug_thread* FirstFreeThread;
    open_debug_block* FirstFreeBlock;

    render_group RenderGroup;
    real32 LeftEdge;
    real32 RightEdge;
    real32 FontScale;
    font_id FontID;
    real32 Width;
    real32 Height;
    uint32 RenderTarget;

    uint32 RootInfoSize;
    char* RootInfo;

    uint32 ToolTipCount;
    char ToolTipText[16][256];
};

internal void DEBUGReset(game_assets* Assets, real32 Width, real32 Height);
internal void DebugOverlay(game_memory* Memory);
internal void RefreshCollation(debug_state* DebugState);
inline debug_id DEBUG_POINTER_ID(void* Pointer) {
    debug_id ID = {Pointer};
    return ID;
}
#define DEBUG_UI_ENABLED 1
void DEBUG_HIT(debug_id ID, real32 ZValue);
bool32 DEBUG_HIGHLIGHTED(debug_id ID, v4* Color);
bool32 DEBUG_REQUESTED(debug_id ID);
#define DEBUG_BEGIN_OPTIONAL_DATA_BLOCK(...) DEBUG_BEGIN_DATA_BLOCK()
void DrawProfileIn(debug_state* DebugState, debug_id GraphID, rectangle2 ProfileRect, v2 MouseP,
                   debug_element* RootElement);
void DrawFrameBars(debug_state* DebugState, debug_id GraphID, rectangle2 ProfileRect, v2 MouseP,
                   debug_element* RootElement);
void* PushSizeWithDeallocation(debug_state* DebugState, memory_index Size,
                               memory_index Alignment = DEFUALT_MEMORY_ALIGNMENT);
inline debug_interaction SetUInt32Interaction(debug_id DebugID, uint32* Target, uint32 Value);
inline debug_interaction SetPointerInteraction(debug_id DebugID, void** Target, void* Value);
bool32 InteractionsIsHot(debug_state* DebugState, debug_interaction B);
rectangle2 DEBUGGetTextSize(debug_state* DebugState, const char* String);
rectangle2 TextOutAt(v2 P, const char* String, v4 Color = {1, 1, 1, 1}, real32 AtZ = 0.0f);
void DrawTopClocksList(debug_state* DebugState, debug_id GraphID, rectangle2 ProfileRect, v2 MouseP,
                       debug_element* RootElement);
#endif